using System;
using System.IO;
using System.Linq;
using System.Speech.Synthesis;
using System.Threading;
using Caliburn.Micro;
using ClientUI.Model;
using Dto.ApiRequests;
using Dto.ApiResponses;
using Rfid.HfLb;
using Sanelib.Common.Helpers;
using Sanelib.Core;
using Sanelib.Dto;
using Sanelib.Net.Http;

namespace ClientUI.ViewModel
{
    public class TrackerViewModel : Screen
    {
        private static readonly object PadLock = new object();
        private readonly SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer();
        private readonly Thread _thread;

        public TrackerViewModel()
        {
            _thread = new Thread(BackThread);
            Firmware.GetInstance().Connect();

            _speechSynthesizer.SelectVoiceByHints(VoiceGender.Male);
            _speechSynthesizer.Volume = 100;
            _speechSynthesizer.Rate = 0;
        }

        protected override void OnActivate()
        {
            Logger.Log(LogType.Info, this, "On Activate Tracker Model");
            Firmware.GetInstance().LoopBack += HfLbKioskLoopBack;
            Firmware.GetInstance().StartPolling(TagType.Member);
            _thread.Start();
            ShowAndLogMessage("Status", "Idle");
            base.OnActivate();
        }

        protected override void OnDeactivate(bool close)
        {
            Logger.Log(LogType.Info, this, "On Deactivate Tracker Model");
            Firmware.GetInstance().LoopBack -= HfLbKioskLoopBack;
            Firmware.GetInstance().StopPolling();
            _thread.Abort();
            base.OnDeactivate(close);
        }

        private void HfLbKioskLoopBack(object sender, HfLbLoopBackEventArg e)
        {
            var tagData = e.TagData;
            if (!tagData.Type.Equals(TagType.Member)) return;
            AddItemToTrack(tagData);
        }

        private string _message = string.Empty;

        public string Message { get { return _message; } }

        private BindableCollection<TrackerItem> _trnsList;
        public BindableCollection<TrackerItem> TrnsList
        {
            get { return _trnsList ?? (_trnsList = new BindableCollection<TrackerItem>()); }
        }

        private void AddItemToTrack(TagData tagData)
        {
            lock (PadLock)
            {
                if (TrnsList.Any(i => i.ItemUid == tagData.Uid))
                {
                    ShowAndLogMessage("AddItemToTrack", "Item already in list.");
                    return;
                }

                TrnsList.Insert(0, new TrackerItem { ItemUid = tagData.Uid, ItemCode = tagData.Code });
                NotifyOfPropertyChange(() => TrnsList);
            }
        }

        public void BackThread()
        {
            try
            {
                while (_thread.IsAlive)
                {
                    var item = GetUnProcessedItem();
                    if (item != null)
                    {
                        TrackItemProc(item);
                        var fileName = string.Format("{0}\\{1}.csv", LogFolder, DateTime.Now.ToString("ddMMyyyy"));
                        File.AppendAllText(fileName, string.Format("{0},{1},{2},{3}", item.ItemUid, item.ItemCode, DateTime.Now, Environment.NewLine));
                    }

                    for (var i = 0; i < TrnsList.Count; i++)
                    {
                        item = TrnsList[i];
                        if (item.CreatedOn > DateTime.Now.AddMinutes(-1)) continue;
                        if (!item.IsProcessed)
                        {
                            var fileName = string.Format("{0}\\{1}_Fault.csv", LogFolder, DateTime.Now.ToString("ddMMyyyy"));
                            File.AppendAllText(fileName, string.Format("{0},{1},{2} of {3},{4},{5}", item.ItemUid, item.ItemCode, DateTime.Now, item.ItemUid, item.Message, Environment.NewLine));
                        }
                        TrnsList.Remove(TrnsList[i]);
                        i--;
                    }

                    TrnsList.Refresh();
                }
            }
            catch (Exception exception)
            {
                Logger.Log(LogType.Error, this, "BackThread:" + exception.GetBaseException().Message, exception);
            }
        }

        private void TrackItemProc(TrackerItem item)
        {
            item.IsProcessed = true;

            ShowAndLogMessage("Track", item.ItemUid);

            var log = new TrackLogForm
            {
                TagId    = item.ItemUid,
                DetectedOn = Formatter.DateAndTime(SystemTime.Now())
            };

            ErrorResult errors;

            var response = Processor.ExecuteRequest<TrackLogForm, TrackLogResponse>(ContentType.JSON, RequestType.POST, log, out errors);

            if (!errors.IsValid)
            {
                item.SetErrorMessage(errors.ToString());
                _speechSynthesizer.SpeakAsync(string.Format("Error. Please contact administrator"));
                ShowAndLogMessage("Tracker", item.Message);
                return;
            }

            _speechSynthesizer.SpeakAsync(string.Format("Welcome {0}", response.Title));

            item.ItemCode = response.Code;
            item.ItemTitle = response.Title;
            item.IsProcessed = true;
            ShowAndLogMessage("Tracker", item.Message);
        }

        private TrackerItem GetUnProcessedItem()
        {
            TrackerItem item;
            lock (PadLock)
            {
                item = TrnsList.LastOrDefault(i => !i.IsProcessed);
            }
            if (item == null)
            {
                ShowAndLogMessage("Status", "Idle");
                Thread.Sleep(3000);
            }
            return item;
        }

        #region Logging

        private static string LogFolder
        {
            get
            {
                var logFolder = AppDomain.CurrentDomain.BaseDirectory + "TrackerLog";
                if (!Directory.Exists(logFolder))
                {
                    Directory.CreateDirectory(logFolder);
                }
                return logFolder;
            }
        }

        private void ShowAndLogMessage(string key, string message)
        {
            var msg = string.Format("{0} - {1}", key, message);
            Logger.Log(LogType.Info, this, msg);
            _message = msg;
            NotifyOfPropertyChange(() => Message);
        }

        #endregion

    }
}
